#!/usr/bin/python2.4
#
# Each job is a control file, it can be a customized control file on the
# filesystem, or it can be retrieved from autotest server.


import os

from logger import logger
import rpc_stubs
import utils


def get_autotest_test_name(path):
  prefix = 'Autotest://'
  if path[:len(prefix)].lower() == prefix.lower():
    return path[len(prefix):]
  return None


class ControlFile(object):
  # Non create job args:
  #  path, job_name, duration, deps_set (set of dependencies)
  #
  # Essential create job args:
  #  control_file, control_type, synch_count, dependencies, timeout

  def __init__(self, path, job_name=None, duration=None, **create_job_args):
    # path to the control file
    self.path = path
    if not job_name:
      test_name = get_autotest_test_name(path)
      if test_name is not None:
        job_name = test_name
      else:
        job_name = os.path.basename(path)
    # job_name = job label, how we call it
    self.job_name = job_name
    # duration is the time the test runs (in hours)
    self.duration = duration
    # create job args
    self.create_job_args = create_job_args
    # set of dependencies
    self.deps_set = None
    # mtime and size for local control file
    self.mtime_and_size = None

  def __repr__(self):
    return '%s(job_name=%r, path=%r)' % (
        type(self).__name__, self.job_name, self.path)

  def __getattr__(self, name):
    try:
      return self.create_job_args[name]
    except KeyError:
      raise AttributeError('%r object has no attribute %r' % (
                           type(self).__name__, name))

  @property
  def autotest_test_name(self):
    return get_autotest_test_name(self.path)

  def setup(self):
    test_name = self.autotest_test_name
    if test_name is not None:
      generated_test = rpc_stubs.afe.generate_control_file(tests=[test_name])
      # generate_control_file returns a dict with the following keys:
      # control_file: str, The control file text.
      # is_server: bool -> control_type
      # synch_count: int
      # dependencies: a list of the names of labels on which the job depends.
      generated_test = dict((str(k), v) for k, v in generated_test.iteritems())
      if generated_test.pop('is_server'):
        generated_test['control_type'] = 'Server'
      else:
        generated_test['control_type'] = 'Client'
      self.create_job_args.update(generated_test)
    else:
      self.mtime_and_size = utils.mtime_and_size(self.path)
      self.create_job_args['control_file'] = file(self.path).read()
      self.create_job_args.setdefault('control_type', 'Server')
      self.create_job_args.setdefault('synch_count', 1)
      self.create_job_args.setdefault('dependencies', [])

    self.deps_set = set(self.create_job_args['dependencies'])

    if not self.duration:
      if test_name is not None:
        raw_tests = rpc_stubs.afe.get_tests(name=test_name)
        if len(raw_tests) != 1:
          logger.fatal("Test '%s' is not found" % test_name)
        test_time_to_duration = {
            'SHORT': 1,
            'MEDIUM': 4,
            'LONG': 12
        }
        self.duration = test_time_to_duration[raw_tests[0]['test_time']]
      else:
        self.duration = 1

    timeout = self.duration + min(self.duration * 2, 24)
    self.create_job_args.setdefault('timeout', timeout)

  def reload_control_file(self):
    test_name = self.autotest_test_name
    if test_name is not None:
      generated_test = rpc_stubs.afe.generate_control_file(tests=[test_name])
      control_file = generated_test['control_file']
      changed = control_file != self.create_job_args['control_file']
    else:
      mtime_and_size = utils.mtime_and_size(self.path)
      changed = mtime_and_size != self.mtime_and_size
      if changed:
        self.mtime_and_size = mtime_and_size
        control_file = file(self.path).read()
        changed = control_file != self.create_job_args['control_file']
    if changed:
      logger.error("Control file of '%s' has been updated" % self.job_name)
      self.create_job_args['control_file'] = control_file

  def create_job(self, create_job_args=(), **overrides):
    self.reload_control_file()
    create_job_args = dict(create_job_args)
    create_job_args.update(self.create_job_args)
    utils.update_dict(create_job_args, overrides)
    logger.error("Scheduling '%s' on hosts %s" %
                 (self.job_name,
                  utils.pformat_array(create_job_args['hosts'])))
    if len(create_job_args['hosts']) % create_job_args['synch_count']:
      logger.error('Number of hosts %d is not a multiple of synch_count %d' %
                   (len(create_job_args['hosts']),
                    create_job_args['synch_count']))
    job_id = rpc_stubs.afe.create_job(**create_job_args)
    logger.error('Job ID: %s' % job_id)
    return job_id

  def pformat(self, include_control_file=False):
    results = ['%s:%r' % (attr, getattr(self, attr))
               for attr in ('job_name', 'path', 'duration')]
    results.extend('%s:%r' % (key, value)
                   for key, value in self.create_job_args.iteritems()
                   if key != 'control_file')

    if include_control_file:
      results.append('control_file:\n%s' %
                     self.create_job_args['control_file'])

    return ' '.join(results)
